package com.itheima.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.bean.Dish;
import com.itheima.bean.DishFlavor;
import com.itheima.common.PageParam;
import com.itheima.dao.DishDao;
import com.itheima.dto.DishDto;
import com.itheima.service.CategoryService;
import com.itheima.service.DishFlavorService;
import com.itheima.service.DishService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Transactional
@Service
public class DishServiceImpl extends ServiceImpl<DishDao , Dish> implements DishService {


    @Autowired
    private RedisTemplate redisTemplate;


    //注入dishflavorservice
    @Autowired
    private DishFlavorService dishFlavorService;

    @Autowired
    private CategoryService categoryService;

    /**
     * 根据分类id和状态值来查询菜品数据，和口味数据
     *
     * @param categoryId
     * @param status
     * @return
     */
    @Override
    public List<DishDto> findByCategoryId(Long categoryId, Integer status) {

        String key = "dish_"+categoryId;

        // 缓存的思路是： 1. 一上来就先从缓存里面拿数据，如果有就直接返回，
        // 2.  如果没有，就去查询数据库，把查询到的数据存储到缓存里面，并且返回给页面
        String jsonData = (String) redisTemplate.opsForValue().get(key);
        if(jsonData != null){
            //表示缓存里面有数据，直接返回！
            System.out.println("缓存里面有数据，直接返回缓存的数据:::" + categoryId);
            return JSON.parseObject(jsonData ,List.class);
        }
        System.out.println("缓存里面没有有数据，现在要去查询数据库:::" + categoryId);

        //1. 查询菜品数据
        LambdaQueryWrapper<Dish> lqw = new LambdaQueryWrapper<>();

        // 设置条件。
        lqw.eq(Dish::getCategoryId , categoryId);
        lqw.eq(status != null , Dish::getStatus , status);
        List<Dish> dishList = this.list(lqw);

        //2. 需要构建一个List<DishDto> ，这样就能装Dish对象，也能装口味数据
        List<DishDto> dtoList = dishList.stream().map(dish -> {

            //1. 创建DishDto
            DishDto dto = new DishDto();

            //2. 拷贝数据，把dish拷贝到dto这里
            BeanUtils.copyProperties(dish, dto);

            //3. 还缺少口味数据
            LambdaQueryWrapper<DishFlavor> dfQW = new LambdaQueryWrapper<>();
            dfQW.eq(DishFlavor::getDishId, dish.getId());
            List<DishFlavor> flavorList = dishFlavorService.list(dfQW);

            dto.setFlavors(flavorList);

            return dto;
        }).collect(Collectors.toList());


        //在这里把数据保存到redis当中。
        redisTemplate.opsForValue().set( key, JSON.toJSONString(dtoList));


        return dtoList;
    }

    /**
     * 添加菜品
     *  1. 添加菜品稍微有点不一样，因为这份数据要分别往两张表里面存储： dish 表， dish_flavor 表
     *  2. 添加顺序：
     *      2.1 先往菜品表添加数据
     *
     *      2.2 再往口味表添加数据
     *          2.2.1 再添加口味的时候，必须设置这个口味属于哪个菜品，要设置菜品的id。
     *
     * @param dishDto
     * @return
     */
    @Override
    public int add(DishDto dishDto) {

        //1. 先添加菜品:: 只要添加成功，MP 会自动把id填充到对象里面去。
        int row = getBaseMapper().insert(dishDto);

        //2. 后添加口味
        //2.1 得到口味的集合数据。对集合里面的每一个口味都要做dishId的设置工作。 map() 用来映射的。
        /*List<DishFlavor> flavorList = dishDto.getFlavors().stream().map(new Function<DishFlavor, DishFlavor>() {

            @Override
            public DishFlavor apply(DishFlavor dishFlavor) {
                //设置口味属于哪个菜品
                dishFlavor.setDishId(dishDto.getId());
                return dishFlavor;
            }
        }).collect(Collectors.toList());*/
        boolean result = true;
        if(dishDto.getFlavors()!=null && dishDto.getFlavors().size() >0 ){
            List<DishFlavor> flavorList1 = dishDto.getFlavors().stream().map(dishFlavor -> {
                dishFlavor.setDishId(dishDto.getId());
                return dishFlavor;
            }).collect(Collectors.toList());

            result = dishFlavorService.saveBatch(flavorList1);
        }

        if(row > 0 && result){
            //添加成功了，那么就要去删除缓存数据
            redisTemplate.delete("dish_"+dishDto.getCategoryId());
            return 1 ;
        }
        return 0 ;
    }

    /**
     * 菜品分页
     *  1. 菜品分页，只是去查询菜品表而已，得到的固然是菜品的数据，但是页面还需要用到分类的数据。
     *  2. 这里遇到两个问题：
     *      2.1 怎么才能查询到菜品的分类名称？
     *          2.1.1 当我们查询出来10条【假设每页10条数据】菜品数据之后，菜品数据身上是有分类的id值。
     *          2.1.2 那么就可以拿着分类的id值去查询分类表，得到该分类的所有信息，当然也就包括分类的名称
     *      2.2 如何把菜品的数据和分类的名称一块返回给前端呢？
     *          2.2.1 需要把菜品的数据和分类的名称数据看成是一个整体，使用一个全新的JavaBean来包装它们。
     *          2.2.2 可以使用DishDto来包装：
     *              1. 它继承了Dish类型，所以它能够包装Dish的数据
     *              2. 它也声明了一个新的属性： categoryName 这个可以包装分类的名称。
     *
     *  3. 一会代码要下沉到Service层去写！并且service层的返回值必须是： IPage<DishDto>
     * @param pageParam
     * @return
     */
    @Override
    public IPage<DishDto> page(PageParam pageParam) {

        //1. 先查询出来Dish表的分页数据。

        //1.1 构建分页的对象
        IPage<Dish> page = new Page<>(pageParam.getPage() , pageParam.getPageSize());

        //1.2 构建条件对象
        LambdaQueryWrapper<Dish> lqw = new LambdaQueryWrapper<>();

        //1.3 封装条件
        lqw.like(pageParam.getName() != null , Dish::getName , pageParam.getName());

        //1.4 执行查询操作:: 不能直接返回这个dishPage, 因为它没有分类的名称
        IPage<Dish> dishPage = getBaseMapper().selectPage(page, lqw);

        //2. 需要构建一个IPage<DishDto> ， 直接返回它。

        //2.1 构建一个新的IPage<DishDto>
        IPage<DishDto> dishDtoPage = new Page<>();

        //2.2 考虑给这个dishDtoPage 组装数据： 总记录数， 当前页的集合数。
        dishDtoPage.setTotal(dishPage.getTotal());

        //必须要把dishPage里面的10条数据，映射成10个DishDto，映射之后，要根据分类id去查询分类数据。采用流的方式来做！
        List<DishDto> dtoList = dishPage.getRecords().stream().map(dish -> {
            //a. 构建一个DishDto
            DishDto dishDto = new DishDto();

            //b. 把dish的数据拷贝到dishDto 属性拷贝的方式，把数据拷贝过来
            //参上一：源对象， 参数二： 目标对象。只拷贝同名属性。
            BeanUtils.copyProperties(dish, dishDto);


            //c. 没有分类的数据
            String categoryName = categoryService.getById(dish.getCategoryId()).getName();
            dishDto.setCategoryName(categoryName);


            //d. 没有口味的数据:: 再去查询口味数据，然后封装到dto里面去
            LambdaQueryWrapper<DishFlavor> dfQW = new LambdaQueryWrapper<>();
            dfQW.eq(DishFlavor::getDishId ,dish.getId() );
            List<DishFlavor> flavorList = dishFlavorService.list(dfQW);
            dishDto.setFlavors(flavorList);


            //返回dto对象。
            return dishDto;
        }).collect(Collectors.toList());

        //把集合装到Page对象里面去。
        dishDtoPage.setRecords(dtoList);
        return dishDtoPage;
    }

    /**
     * 更新菜品
     *  1. 更新菜品需要更新两张表的数据：1.菜品表 ，2.菜品口味表
     *  2. 更新菜品表
     *      2.1 更新菜品表，其实很简单，就按照正常的根据id来更新即可
     *  3. 更新口味表
     *      3.1 因为口味的数据变化太大，很难去计较到底是怎么更新的。
     *      3.2 所以可以把这个菜品原来的所有口味都删除掉
     *      3.3 然后把页面传递过来的口味再添加进去即可！
     *
     * @param dishDto
     * @return
     */
    @Override
    public int update(DishDto dishDto) {

        //1. 更新菜品表
        boolean result1 = this.updateById(dishDto);

        //2. 更新口味表

        //2.1 先删除口味
        LambdaQueryWrapper<DishFlavor> lqw = new LambdaQueryWrapper<>();
        lqw.eq(DishFlavor::getDishId , dishDto.getId());
        dishFlavorService.remove(lqw);

        //2.2 再添加口味

        //2.2.1 获取口味的集合
        List<DishFlavor> flavorList = dishDto.getFlavors();

        //2.2.2 判断有没有口味
        boolean result3 = true;
        if( flavorList!=null && flavorList.size() > 0){

            List<DishFlavor> list = flavorList.stream().map(dishFlavor -> {
                //设置口味属于哪个菜品
                dishFlavor.setDishId(dishDto.getId());

                return dishFlavor;
            }).collect(Collectors.toList());

            //保存口味数据
            result3 = dishFlavorService.saveBatch(list);
        }

        //如果菜品更新成功，口味添加也成功，就表示成功！

        if(result1 && result3){
            //更新数据库成功，也要把缓存的数据给删除了
            redisTemplate.delete("dish_"+dishDto.getCategoryId());
            return 1;
        }

        return 0 ;
    }


   /* @Override
    public int add(DishDto dishDto) {

        //1. 先添加菜品:: 只要添加成功，MP 会自动把id填充到对象里面去。
        int row = getBaseMapper().insert(dishDto);

        //2. 后添加口味
        //2.1 获取每一个口味数据
        List<DishFlavor> flavorList = dishDto.getFlavors();

        boolean flavorResult =true;

        //2.2 只有当有口味的时候，才需要去添加到口味表，如果没有口味数据，那么就不用添加了。
        if(flavorList != null && flavorList.size() > 0 ){
            for (DishFlavor dishFlavor : flavorList) {
                //给每一个口味设置属于什么菜
                dishFlavor.setDishId(dishDto.getId());

                //单独的保存
                flavorResult = dishFlavorService.save(dishFlavor);
            }
        }


        //2.2 把口味数据【集合】批量的添加到口味表里面去
        //dishFlavorService.saveBatch(flavorList);
        return (row > 0 && flavorResult) ? 1 : 0 ;
    }*/
}
